/*
 ------------------------------------------------------------------------------
	willy-player - a simple music player based on SDL.

	Copyright (C) 2011 Pierre Veber
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software Foundation,
	Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
------------------------------------------------------------------------------
*/

#include "precomp.h"

/*
 ------------------------------------------------------------------------------
	AudioLib_t definition. Includes an audio list, root path, and
	FAST HEAPs (see fastheap.h)
	used to store pathes, filenames, tags of media records.
------------------------------------------------------------------------------
*/

#define MEDIA_HEAP_PAGE_SIZE	262144
#define FILE_HEAP_PAGE_SIZE		262144
#define DIR_HEAP_PAGE_SIZE		524288
#define TAG_HEAP_PAGE_SIZE		524288

struct AudioLib_t
{
	char* pszPath;

	FastHeap_t* pDirHeap;
	FastHeap_t* pFileHeap;
	FastHeap_t* pMediaHeap;
	FastHeap_t* pTagHeap;

	HandleLibStatus fpStatus;
	
	AudioList_t list;
};

/*
 ------------------------------------------------------------------------------
	CWPDecoders list management

		decoderIndex:
			returns index in global static decoders list

------------------------------------------------------------------------------
*/
static CWPDecoder* ppDecoders[] = { _g_pMadDecoder };
CWPDecoder** _g_ppDecoders = ppDecoders;

int decoderIndex(char* pszFileName)
{
	char* pExt = strrchr(pszFileName, '.');
	
	for (int i=0; (i < sizeof(ppDecoders)/sizeof(CWPDecoder*)) && (pExt != NULL); i++)
	{
		char* pSup = (char*)_g_ppDecoders[i]->extensions();
		char* p = ++pExt;
		
		while ( (*pSup != '\0') &&  (*pSup != '|') )
		{
			if ( tolower(*pSup) != tolower(*p) )
			{
				pSup = strchr(pSup, '|');
				if (pSup == NULL)
				{
					return ENODEC;
				}
				else
				{
					pSup++;
					p = pExt;
					continue;
				}
			}
			pSup++;
			p++;
		}
		if ( p != pExt )
		{
			return i;
		}
	}
	return ENODEC;
}

/*
 ------------------------------------------------------------------------------
	AudioFile_t basic manipulation routines.

		_audio_file_new:
			allocation on Media Heap. Memsets to 0.

------------------------------------------------------------------------------
*/

AudioFile_t* _audio_file_new(FastHeap_t* pMediaHeap)
{
	AudioFile_t* pOut = (AudioFile_t*)wp_fast_heap_alloc(pMediaHeap, sizeof(AudioFile_t));
	if (pOut != NULL)
	{
		memset(pOut, 0, sizeof(AudioFile_t));
		pOut->iDecoder = ENODEC;
	}
	return pOut;
}

/*
 ------------------------------------------------------------------------------
	AudioLib_t basic manipulation routines.

		libCreate:
			Standard allocation, copies (strdup) path in structure.
			Allocates Dir and File Heaps.
		libFree:
			Frees members and node.
			Does not deallocate Dir and File Heaps.
------------------------------------------------------------------------------
*/

AudioLib_t* libCreate(const char* pszPath)
{
	AudioLib_t* pOut = (AudioLib_t*)malloc(sizeof(AudioLib_t));
	if (pOut != NULL)
	{
		memset(pOut, 0, sizeof(AudioLib_t));
		pOut->pszPath = _strdup(pszPath);
	}
	return pOut;
}

void libFree(AudioLib_t* pLib)
{
	if (pLib != NULL)
	{
		if (pLib->pszPath != NULL)
		{
			free(pLib->pszPath);
		}
		playlistFree(&(pLib->list));
		if (pLib->pDirHeap != NULL)
		{
			wp_fast_heap_free(pLib->pDirHeap);
		}
		if (pLib->pFileHeap != NULL)
		{
			wp_fast_heap_free(pLib->pFileHeap);
		}
		if (pLib->pMediaHeap != NULL)
		{
			wp_fast_heap_free(pLib->pMediaHeap);
		}
		if (pLib->pTagHeap != NULL)
		{
			wp_fast_heap_free(pLib->pTagHeap);
		}
	}
}

/*
 ------------------------------------------------------------------------------
	Audio Library File Search and Parsing

		libFill:
			Entry point. Creates thread and passes pointer to AudioLib_t.
			Thread procedure is _media_fill_proc.

		_audio_fill_proc:
			Executed in a separate thread. Walks through the Audio Library 
			folder, and for each file found, calls _on_file_found handler.
			After the walk is finished, signals that Audio Library dir mode 
			is accessible. Then starts audio file tag reading by calling 
			_audio_tag_fill.

 ------------------------------------------------------------------------------
*/

static int _on_file_found(void* pData, char* pszBase, char* pszFileName)
{
	AudioLib_t* pLib = (AudioLib_t*)pData;
	int dec = decoderIndex(pszFileName);

	if (dec != ENODEC)
	{
		if (pLib->pMediaHeap == NULL)
		{
			pLib->pMediaHeap = wp_fast_heap_new(MEDIA_HEAP_PAGE_SIZE);
		}
		if (pLib->pFileHeap == NULL)
		{
			pLib->pFileHeap = wp_fast_heap_new(FILE_HEAP_PAGE_SIZE);
		}

		AudioFile_t* pFile = _audio_file_new(pLib->pMediaHeap);
		if (pFile != NULL)
		{
			pFile->iDecoder = dec;
			pFile->pszFullPath = (char*)wp_fast_heap_dup(pLib->pFileHeap, pszFileName, strlen(pszBase)+strlen(pszFileName)+2);
			if (pFile->pszFullPath != NULL)
			{
				sprintf(pFile->pszFullPath, "%s\\%s", pszBase, pszFileName);
			}
			if (pLib->list.pTail != NULL)
			{
				pLib->list.pTail->pNext = pFile;
				pLib->list.pTail = pFile;
			}
			else
			{
				pLib->list.pHead = pLib->list.pTail = pFile;
			}
			pLib->list.nFiles++;
		}
	}
	return 0;
}

void* _malloc_dir(size_t size, void* pData)
{
	AudioLib_t* pLib = (AudioLib_t*)pData;
	return wp_fast_heap_alloc(pLib->pDirHeap, size);
}

void* _malloc_id3(size_t size, void* pData)
{
	AudioLib_t* pLib = (AudioLib_t*)pData;
	return wp_fast_heap_alloc(pLib->pTagHeap, size);
}

static int _audio_fill_proc(HThread hThread, void* pData)
{
	DWORD dwStart, dwEnd;
	AudioFile_t* pFile;
	AudioLib_t* pLib = (AudioLib_t*)pData;

	if (pLib->pDirHeap == NULL)
	{
		pLib->pDirHeap = wp_fast_heap_new(DIR_HEAP_PAGE_SIZE);
	}

	dwStart = GetTickCount();
	_walk(pLib->pszPath, pLib, _on_file_found, _malloc_dir);
	dwEnd = GetTickCount();
	
	TRACE2("LIB_FILE_ACCESS - %d files found in %d milliseconds.\n", pLib->list.nFiles, (int)(dwEnd-dwStart));

	/* signal that library is accessible in dir mode  */
	if (pLib->fpStatus != NULL)
	{
		pLib->fpStatus(pLib, LIB_STATUS_FILE_ACCESS);
	}

	/* starts filling tag information */
	pFile = pLib->list.pHead;

	if (pLib->pTagHeap == NULL)
	{
		pLib->pTagHeap = wp_fast_heap_new(TAG_HEAP_PAGE_SIZE);
	}

	dwStart = GetTickCount();
	while (pFile != NULL)
	{
		if (pFile->iDecoder != ENODEC)
		{
			_g_ppDecoders[pFile->iDecoder]->getInfo(pFile, _malloc_id3, pLib);
		}
		pFile = pFile->pNext;
	}
	dwEnd = GetTickCount();
	
	TRACE2("LIB_ID3_ACCESS - %d files processed in %d milliseconds.\n", pLib->list.nFiles, (int)(dwEnd-dwStart));

	/* signal that library is accessible in id3 tag mode  */
	if (pLib->fpStatus != NULL)
	{
		pLib->fpStatus(pLib, LIB_STATUS_ID3_ACCESS);
	}

	return pLib->list.nFiles;
}

int libFill(AudioLib_t* pLib, HandleLibStatus fp)
{
	HThread hThread;

	pLib->fpStatus = fp;
	hThread	= CreateThread(_audio_fill_proc, pLib);
	if (hThread == NULL)
	{
		TRACE("Unable to create media fill thread.\n");
		return 1;
	}
	return 0;
}

/*
 ------------------------------------------------------------------------------

 
 ------------------------------------------------------------------------------
*/

HAudioList playlistCreate()
{
	return NULL;
}

HAudioList playlistFromLib(AudioLib_t* pLib)
{
	if (pLib != NULL)
	{
		return &(pLib->list);
	}
	return NULL;
}

void playlistAdd(HAudioList hList, HAudioFile hFile)
{

}

HAudioFile playlistLookupFile(AudioList_t* pList, const char* pszFilePath)
{
	AudioFile_t* pFile = pList->pHead;
	while (pFile != NULL)
	{
		if (strcmp(pFile->pszFullPath, pszFilePath) == 0)
		{
			return pFile;
		}
		pFile = pFile->pNext;
	}
	return NULL;
}

void playlistFree(HAudioList hList)
{

}
